﻿
/******************************************************************
       
 this._dragHelper = new DragDropHelper(label_CustomDrag);   //初始化这个自定义拖拽器
  
 -----------------------------------------------------------
  
 private void canvas1_Drop(object sender, DragEventArgs e) //通过DragData.Data 拿到拖拽的数据对象
 {
     ttt.Text = (DragData.Data as Label).Content.ToString();
 }
 
 private void canvas1_DragEnter(object sender, DragEventArgs e)
 {
     _dragHelper.IsVirtualDrop = true;                     //设置拖拽标志是否有效
 }

 private void canvas1_DragLeave(object sender, DragEventArgs e)
 {
     _dragHelper.IsVirtualDrop = false;                    //设置拖拽标志是否有效
 }
 
 ******************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace WpfDataBingding.CustomDragDrop
{

    internal class PeHostWin32
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }
        }

        public static Int32 GWL_EXSTYLE = -20;
        public static Int32 WS_EX_LAYERED = 0x00080000;
        public static Int32 WS_EX_TRANSPARENT = 0x00000020;


        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool GetCursorPos(out POINT pt);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern Int32 GetWindowLong(IntPtr hWnd, Int32 nIndex);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern Int32 SetWindowLong(IntPtr hWnd, Int32 nIndex, Int32 newVal);

    }

    /// <summary>
    /// 全局数据存储
    /// </summary>
    internal static class DragData
    {
        public static object Data = null;
    }

    internal class CustomDragDropHelper : DependencyObject
    {

        private UIElement _dragSource;
        private bool _isDragging;
        private Point _startPoint;

        private Window _dragdropWindow;

        public CustomDragDropHelper(UIElement source)
        {
            this._dragSource = source;
            WireEvents(source);
            this.colorBackTransparent = new Color();
            this.colorBackTransparent.A = 1;
        }

        /// <summary>
        /// connect with Event binding
        /// </summary>
        /// <param name="uie"></param>
        private void WireEvents(UIElement uie)
        {
            if (uie != null)
            {
                uie.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
                uie.PreviewMouseMove += new System.Windows.Input.MouseEventHandler(DragSource_PreviewMouseMove);
            }

        }

        void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _startPoint = e.GetPosition(null);

            //第一次拖拽，传递事件
            fe_MouseLeftButtonDown(this._dragSource, e);
        }

        /// <summary>
        /// 启动StartDrag
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DragSource_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {

            if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
            {
                Point position = e.GetPosition(null);
                if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
                {
                    this.StartDrag(e);
                }
            }
        }

        bool? _IsVirtualDrop = null;
        //是否为有效Drop
        public bool? IsVirtualDrop
        {
            get
            {
                return _IsVirtualDrop;
            }
            set
            {
                _IsVirtualDrop = value;
            }
        }

        protected bool IsDragging
        {
            get
            {
                return _isDragging;
            }
            set
            {
                _isDragging = value;
            }
        }

        protected DragDropEffects _allowedEffects = DragDropEffects.Copy | DragDropEffects.Move;
        public DragDropEffects AllowedEffects
        {
            get
            {
                return _allowedEffects;
            }
            set
            {
                _allowedEffects = value;
            }
        }

        protected bool AllowsLink
        {
            get
            {
                return ((this.AllowedEffects & DragDropEffects.Link) != 0);
            }
        }

        protected bool AllowsMove
        {
            get
            {
                return ((this.AllowedEffects & DragDropEffects.Move) != 0);
            }
        }

        protected bool AllowsCopy
        {
            get
            {
                return ((this.AllowedEffects & DragDropEffects.Copy) != 0);
            }
        }

        protected bool _mouseLeftScope = false;

        /// <summary>
        /// 主窗口背景的设置
        /// 也可以设置Opacity来实现次功能
        /// </summary>
        private Color colorBackTransparent;

        bool _canDrag = true;
        DragDropEffects _effects = DragDropEffects.None, _resultEffects = DragDropEffects.None;
        IDataObject _data = null;
        void StartDrag(MouseEventArgs args)
        {
            if (!_canDrag)
            {
                sb_Completed(null, null);
                return;
            }
            _canDrag = false;
            sb_Completed(null, null);
            _data = null;
            UIElement dragelement = null;
            dragelement = args.Source as UIElement;            

            /////////////////////////////////////////////////////////////////////// DragData.Data
            _data = new DataObject(typeof(UIElement).ToString(), dragelement);
            if (dragelement == null || _data == null)
                return;
            if (dragelement is Visual)
            {
                DragData.Data = dragelement;
            }
            else
            {
                this.IsDragging = false;

                return;
            }
            QueryContinueDragEventHandler queryContinue = null;
            GiveFeedbackEventHandler giveFeedback = null;

            _effects = GetDragDropEffects();

            DragDrop.AddPreviewQueryContinueDragHandler(_dragSource, queryContinue = new QueryContinueDragEventHandler(onQueryContinueDrag));
            DragDrop.AddGiveFeedbackHandler(_dragSource, giveFeedback = new GiveFeedbackEventHandler(onGiveFeedback));
            IsDragging = true;


            bool isReady = CreateDragDropWindow(dragelement);
            if (isReady == false)
            {
                IsDragging = false;
                return;
            }
            _dragdropWindow.Show();

            System.Drawing.Rectangle fff = System.Windows.Forms.Screen.GetWorkingArea(new System.Drawing.Point(0, 0));

            try
            {
                _resultEffects = DragDrop.DoDragDrop(_dragSource, _data, _effects);
            }
            catch { _resultEffects = DragDropEffects.None; }
            finally
            {
                _resultEffects = DragDropEffects.None;
            }
            DestroyDragDropWindow();
            DragDrop.RemovePreviewQueryContinueDragHandler(_dragSource, onQueryContinueDrag);
            DragDrop.AddGiveFeedbackHandler(_dragSource, onGiveFeedback);
            IsDragging = false;
            DragFinished(_resultEffects);
            _canDrag = true;
        }


        DragDropEffects GetDragDropEffects()
        {
            DragDropEffects effects = DragDropEffects.None;
            bool ctrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
            bool shift = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);

            if (ctrl && shift && this.AllowsLink)
                effects |= DragDropEffects.Link;
            else if (ctrl && this.AllowsCopy)
                effects |= DragDropEffects.Copy;
            else if (this.AllowsMove)
                effects |= DragDropEffects.Move;

            return effects;
        }

        void onGiveFeedback(object sender, GiveFeedbackEventArgs args)
        {
            args.UseDefaultCursors = false;
            args.Handled = true;
        }

        void onQueryContinueDrag(object sender, QueryContinueDragEventArgs e)
        {
            UpdateWindowLocation();
        }

        protected void DestroyDragDropWindow()
        {
            if (this._dragdropWindow != null)
            {
                //if (_canAnimation)
                //{
                //    _canAnimation = false;
                //    if (_IsVirtualDrop == false)
                //    {
                //        Storyboard sb = new Storyboard();
                //        sb.Completed += new EventHandler(sb_Completed);

                //        DoubleAnimation daL = new DoubleAnimation(_dragdropWindow.Left, ((double)_pScene.X - _pointClickInTheHost.X), new Duration(new TimeSpan(4000000)));
                //        sb.Children.Add(daL);
                //        Storyboard.SetTargetProperty(daL, new PropertyPath(Window.LeftProperty));

                //        DoubleAnimation daT = new DoubleAnimation(_dragdropWindow.Top, ((double)_pScene.Y - _pointClickInTheHost.Y), new Duration(new TimeSpan(4000000)));
                //        sb.Children.Add(daT);
                //        Storyboard.SetTargetProperty(daT, new PropertyPath(Window.TopProperty));

                //        _dragdropWindow.BeginStoryboard(sb);
                //    }
                //    else if (_IsVirtualDrop == true)
                //    {
                //        ScaleTransform ro = new ScaleTransform();
                //        ro.CenterX = _pointClickInTheHost.X;
                //        ro.CenterY = _pointClickInTheHost.Y;
                //        _dragdropWindow.RenderTransform = ro;

                //        DoubleAnimation da = new DoubleAnimation(1, 0, new Duration(TimeSpan.Parse("00:00:0.15")));
                //        da.Completed += new EventHandler(sb_Completed);
                //        ro.BeginAnimation(ScaleTransform.ScaleXProperty, da);
                //        ro.BeginAnimation(ScaleTransform.ScaleYProperty, da);
                //    }
                //    else 
                    {
                        sb_Completed(null, null);
                    }
                //}

            }
        }

        bool _canAnimation = true;
        void sb_Completed(object sender, EventArgs e)
        {
            if (_dragdropWindow != null)
            {
                this._dragdropWindow.Close();
                this._dragdropWindow = null;
            }
            _IsVirtualDrop = _IsVirtualDrop == null ? (bool?)null : (bool?)false;
            _canAnimation = true;
        }

        /// <summary>
        /// Create the DragDrop Window
        /// </summary>
        /// <param name="dragElement"></param>
        protected bool CreateDragDropWindow(Visual dragElement)
        {
            this.CreateDragWindow(dragElement);
            UpdateWindowLocation();
            return true;
        }

        Grid _gOk = null, _gNone = null;
        private void CreateDragWindow(Visual dragElement)
        {
            this._dragdropWindow = new Window();
            _dragdropWindow.WindowStyle = WindowStyle.None;
            _dragdropWindow.AllowsTransparency = true;
            _dragdropWindow.AllowDrop = false;
            _dragdropWindow.Background = null;
            _dragdropWindow.IsHitTestVisible = false;
            _dragdropWindow.SizeToContent = SizeToContent.WidthAndHeight;
            _dragdropWindow.Topmost = true;
            _dragdropWindow.ShowInTaskbar = false;
            FrameworkElement fe = dragElement as FrameworkElement;
            if (fe != null)
            {
                fe.MouseLeftButtonDown += new MouseButtonEventHandler(fe_MouseLeftButtonDown);
            }

            _dragdropWindow.SourceInitialized += new EventHandler(
            delegate(object sender, EventArgs args)
            {

                //TODO assert that we can do this.. 
                PresentationSource windowSource = PresentationSource.FromVisual(this._dragdropWindow);
                IntPtr handle = ((System.Windows.Interop.HwndSource)windowSource).Handle;

                Int32 styles = PeHostWin32.GetWindowLong(handle, PeHostWin32.GWL_EXSTYLE);
                PeHostWin32.SetWindowLong(handle, PeHostWin32.GWL_EXSTYLE, styles | PeHostWin32.WS_EX_LAYERED | PeHostWin32.WS_EX_TRANSPARENT);

            });

            Grid g = new Grid();
            Rectangle r = new Rectangle();
            r.Width = ((FrameworkElement)dragElement).ActualWidth;
            r.Height = ((FrameworkElement)dragElement).ActualHeight;
            VisualBrush vb = new VisualBrush(dragElement);
            vb.Stretch = Stretch.None;
            r.Fill = vb;
            g.Children.Add(r);

            //Path _pathOk = new Path();
            //_pathOk.Width = _pathOk.Height = 14;
            //_pathOk.Stroke = Brushes.White;
            //_pathOk.StrokeThickness = 2;
            //_pathOk.Data = Geometry.Parse(@"M 3,5 L 6,10 L 11,3 M0,0 L14,0 L14,14 L0,14 Z");
            //_gOk = new Grid();
            //_gOk.Visibility = Visibility.Collapsed;
            //_gOk.VerticalAlignment = VerticalAlignment.Top;
            //_gOk.HorizontalAlignment = HorizontalAlignment.Left;
            //_gOk.Background = Brushes.Green;
            //_gOk.Margin = new Thickness(1);
            //_gOk.Children.Add(_pathOk);

            //Path _pathNone = new Path();
            //_pathNone.Width = _pathNone.Height = 14;
            //_pathNone.Stroke = Brushes.White;
            //_pathNone.StrokeThickness = 2;
            //_pathNone.Data = Geometry.Parse(@"M 3,3 L 11,11 M 11,3 L3,11 M0,0 L14,0 L14,14 L0,14 Z");
            //_gNone = new Grid();
            //_gNone.Visibility = Visibility.Collapsed;
            //_gNone.VerticalAlignment = VerticalAlignment.Top;
            //_gNone.HorizontalAlignment = HorizontalAlignment.Left;
            //_gNone.Background = Brushes.Red;
            //_gNone.Margin = new Thickness(1);
            //_gNone.Children.Add(_pathNone);

            //g.Children.Add(_gOk);
            //g.Children.Add(_gNone);

            this._dragdropWindow.Content = g;
            r.Opacity = 0.65;
        }

        Point _pointClickInTheHost = new Point();
        PeHostWin32.POINT _pScene = new PeHostWin32.POINT();
        void fe_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            PeHostWin32.GetCursorPos(out _pScene);
            _pointClickInTheHost = e.GetPosition(sender as FrameworkElement);
        }

        /// <summary>
        /// 
        /// </summary>
        protected void UpdateWindowLocation()
        {
            if (this._dragdropWindow != null)
            {
                PeHostWin32.POINT p;
                if (!PeHostWin32.GetCursorPos(out p))
                {
                    return;
                }
                this._dragdropWindow.Left = (double)p.X - _pointClickInTheHost.X;
                this._dragdropWindow.Top = (double)p.Y - _pointClickInTheHost.Y;

                //if ((_resultEffects & DragDropEffects.Move) == DragDropEffects.Move || (_resultEffects & DragDropEffects.Copy) == DragDropEffects.Copy || (_resultEffects & DragDropEffects.Link) == DragDropEffects.Link)
                //{
                //    _gOk.Visibility = Visibility.Visible;
                //    _gNone.Visibility = Visibility.Collapsed;
                //}
                //else if (_resultEffects == DragDropEffects.None)
                //{
                //    _gNone.Visibility = Visibility.Visible;
                //    _gOk.Visibility = Visibility.Collapsed;
                //}
            }
        }
        /// <summary>
        /// When DragFinishe
        /// </summary>
        /// <param name="ret"></param>
        protected void DragFinished(DragDropEffects ret)
        {
            System.Windows.Input.Mouse.Capture(null);
            if (IsDragging)
            {
                DestroyDragDropWindow();
            }

            IsDragging = false;
        }
    }
}
